#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <unistd.h>

#include "YXConstant.h"
#include "YXLog.h"
#include "YXNet.h"

/*
 * 事件状态
 */
typedef struct ApiState {

    // epoll_event 实例描述符
    int epfd;

    // 事件槽
    struct epoll_event *events;

} ApiState;

/**
 * 创建 EPOLL
 */
int yxApiCreate(EventLoop *event_loop) {
    ApiState *state = malloc(sizeof(ApiState));
    if (state == NULL)
        return YX_ERR;

    // 初始化事件槽空间
    state->events = malloc(sizeof(struct epoll_event) * event_loop->set_size);
    if (state->events == NULL) {
        logErrno();
        exit(EXIT_FAILURE);
    }

    // 创建EPOLL实例
    state->epfd = epoll_create1(0);
    if (state->epfd == -1) {
        logErrno();
        exit(EXIT_FAILURE);
    }

    event_loop->api_data = state;

    return YX_OK;
}

void yxApiDelete(EventLoop *event_loop) {
    ApiState *state = event_loop->api_data;

    close(state->epfd);
    free(state->events);
    free(state);
}

/**
 * 添加事件
 * 注意：EventLoop.events[fd].mask 不会修改
 */
int yxApiAddEvent(EventLoop *event_loop, int fd, int mask) {
    ApiState *state = event_loop->api_data;

    int                op = event_loop->events[fd].mask == YX_NET_NONE ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
    struct epoll_event event;
    event.events  = 0;
    event.data.fd = fd;

    mask |= event_loop->events[fd].mask;

    if (mask & YX_NET_READABLE)
        event.events |= EPOLLIN;
    if (mask & YX_NET_WRITABLE)
        event.events |= EPOLLOUT;

    if (epoll_ctl(state->epfd, op, fd, &event) == -1)
        return YX_ERR;

    return YX_OK;
}

/**
 * 删除 fd 事件
 * 注意：EventLoop.events[fd].mask 不会修改
 */
void yxApiDelEvent(EventLoop *event_loop, int fd, int delmask) {
    ApiState          *state = event_loop->api_data;
    int                mask  = event_loop->events[fd].mask & (~delmask);
    struct epoll_event event;
    event.events  = 0;
    event.data.fd = fd;

    if (mask & YX_NET_READABLE)
        event.events |= EPOLLIN;
    if (mask & YX_NET_WRITABLE)
        event.events |= EPOLLOUT;

    if (mask == YX_NET_NONE) {
        epoll_ctl(state->epfd, EPOLL_CTL_DEL, fd, NULL);
    } else {
        epoll_ctl(state->epfd, EPOLL_CTL_MOD, fd, &event);
    }
}

int yxApiPoll(EventLoop *event_loop, long long timeout) {
    ApiState *state = event_loop->api_data;

    int event_nums = epoll_wait(state->epfd, state->events, event_loop->set_size, timeout);
    if (event_nums == -1)
        return 0;

    for (int i = 0; i < event_nums; i++) {
        int                 mask  = 0;
        struct epoll_event *event = state->events + i;
        if (event->events & EPOLLIN)
            mask |= YX_NET_READABLE;
        if (event->events & EPOLLOUT)
            mask |= YX_NET_WRITABLE;
        if (event->events & EPOLLERR)
            mask |= YX_NET_WRITABLE;
        if (event->events & EPOLLHUP)
            mask |= YX_NET_WRITABLE;

        event_loop->fired_events[i].fd   = event->data.fd;
        event_loop->fired_events[i].mask = mask;
    }
    return event_nums;
}